package com.betterjr.modules.acceptbill.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.dubbo.config.annotation.Reference;
import com.betterjr.common.exception.BytterTradeException;
import com.betterjr.common.utils.BTAssert;
import com.betterjr.common.utils.Collections3;
import com.betterjr.common.utils.UserUtils;
import com.betterjr.mapper.pagehelper.Page;
import com.betterjr.modules.acceptbill.dao.ScfAcceptBillDOMapper;
import com.betterjr.modules.acceptbill.entity.ScfAcceptBillDO;
import com.betterjr.modules.account.entity.CustInfo;
import com.betterjr.modules.account.entity.CustOperatorInfo;
import com.betterjr.modules.account.service.CustAccountService;
import com.betterjr.modules.asset.data.AssetConstantCollentions;
import com.betterjr.modules.customer.ICustMechBaseService;
import com.betterjr.modules.document.ICustFileService;
import com.betterjr.modules.joblog.data.LogConstantCollections;
import com.betterjr.modules.version.constant.VersionConstantCollentions;
import com.betterjr.modules.version.service.BaseVersionService;

@Service
public class ScfAcceptBillDOService extends BaseVersionService<ScfAcceptBillDOMapper, ScfAcceptBillDO> {

    @Autowired
    private CustAccountService custAccountService;

    @Reference(interfaceClass = ICustFileService.class)
    private ICustFileService custFileDubboService;

    @Reference(interfaceClass = ICustMechBaseService.class)
    private ICustMechBaseService baseService;

    @Reference(interfaceClass = ICustMechBaseService.class)
    private ICustMechBaseService custMechBaseService;

    /**
     * 新增汇票信息
     */
    public ScfAcceptBillDO addAcceptBill(final ScfAcceptBillDO anAcceptBill, final String anFileList,
            final boolean confirmFlag) {

        BTAssert.notNull(anAcceptBill, "票据登记失败，原因：保存数据异常");
        BTAssert.notNull(anAcceptBill.getCoreCustNo(), "票据登记失败，原因：请选择开票单位");
        BTAssert.notNull(anAcceptBill.getSupplierNo(), "票据登记失败，原因：请选择收款人");
        logger.info("Begin to add addAcceptBill" + UserUtils.getOperatorInfo().getName());
        anAcceptBill.setCoreCustName(custAccountService.queryCustName(anAcceptBill.getCoreCustNo()));
        anAcceptBill.setSupplierName(custAccountService.queryCustName(anAcceptBill.getSupplierNo()));
        anAcceptBill.setInvoiceCorp(anAcceptBill.getCoreCustName());
        anAcceptBill.initAddValue(UserUtils.getOperatorInfo(), confirmFlag);
        // 操作机构设置为供应商
        anAcceptBill.setOperOrg(baseService.findBaseInfo(anAcceptBill.getSupplierNo()).getOperOrg());
        // 保存附件信息
        anAcceptBill.setBatchNo(custFileDubboService.updateCustFileItemInfo(anFileList, anAcceptBill.getBatchNo()));
        this.insertVersion(anAcceptBill);
        logger.info("success to add addAcceptBill" + UserUtils.getOperatorInfo().getName());
        return anAcceptBill;
    }

    /**
     * 
     * @param anModiBill  修改的票据对象详情
     * @param anFileList  文件上次批次好
     * @param anConfirmFlag 是否修改加确认 true 标识确认    false 标识只是修改
     * @return
     */
    public ScfAcceptBillDO saveModifyAcceptBill(ScfAcceptBillDO anModiBill, final String anFileList,
            final boolean anConfirmFlag) {

        BTAssert.notNull(anModiBill, "票据修改失败,原因：对象为空");
        BTAssert.notNull(anModiBill.getRefNo(), "票据修改失败,原因：凭证编号为空");
        BTAssert.notNull(anModiBill.getVersion(), "票据修改失败,原因：票据信息不全");
        logger.info("Begin to modify bill");
        ScfAcceptBillDO bill = this.selectOneWithVersion(anModiBill.getRefNo(), anModiBill.getVersion());
        BTAssert.notNull(bill, "票据修改失败,原因：无法获取票据信息");
        // 校验当前操作员是否是创建此订单的人 并且校验当前订单是否允许修改
        checkOperatorModifyStatus(UserUtils.getOperatorInfo(), bill);
        // 应收账款信息变更迁移初始化
        // order.setCustName(custAccountService.queryCustName(anModiBill.getCustNo()));
        anModiBill.setCoreCustName(custAccountService.queryCustName(anModiBill.getCoreCustNo()));
        anModiBill.setSupplierName(custAccountService.queryCustName(anModiBill.getSupplierNo()));
        anModiBill.setInvoiceCorp(anModiBill.getCoreCustName());
        anModiBill.initModifyValue(bill, anConfirmFlag);
        // 操作机构设置为供应商
        anModiBill.setOperOrg(baseService.findBaseInfo(anModiBill.getSupplierNo()).getOperOrg());

        // 初始版本更改 直接返回
        if (bill.getBusinStatus().equals(VersionConstantCollentions.BUSIN_STATUS_INEFFECTIVE)
                && bill.getDocStatus().equals(VersionConstantCollentions.DOC_STATUS_DRAFT)) {
            if (anConfirmFlag) {
                anModiBill.setDocStatus(VersionConstantCollentions.DOC_STATUS_CONFIRM);
            }
            // 数据存盘
            this.updateByPrimaryKeySelective(anModiBill);
            return anModiBill;
        }
        // 需要升级版本的修改
        // this.updateByPrimaryKeySelective(anOrder);
        if (anConfirmFlag) {
            anModiBill.setDocStatus(VersionConstantCollentions.DOC_STATUS_CONFIRM);
        }
        // ScfOrderDO order2 = this.selectOneWithVersion(order.getRefNo(),order.getVersion());
        anModiBill = this.updateVersionByPrimaryKeySelective(anModiBill, anModiBill.getRefNo(),
                anModiBill.getVersion());
        BTAssert.notNull(anModiBill, "票据修改失败,原因：数据异常,请检查");
        logger.info("success to modify bill" + UserUtils.getOperatorInfo().getName());
        return anModiBill;
    }

    /**
     * 作废当前票据(当前创建者作废)
     * @param refNo
     * @param version
     * @return
     */
    public ScfAcceptBillDO saveAnnulBill(final String refNo, final String version) {

        BTAssert.notNull(refNo, "凭证单号为空!操作失败");
        BTAssert.notNull(version, "数据版本为空!操作失败");
        logger.info("begin to aunul bill" + UserUtils.getOperatorInfo().getName());
        ScfAcceptBillDO bill = this.selectOneWithVersion(refNo, version);
        BTAssert.notNull(bill, "此票据异常!操作失败");
        bill = this.annulOperator(UserUtils.getOperatorInfo(), bill);
        logger.info("success to aunul bill" + UserUtils.getOperatorInfo().getName());
        return bill;
    }

    /**
     * 作废当前票据(同一个公司可以作废) 票据回收作废
     * @param refNo
     * @param version
     * @return
     */
    public ScfAcceptBillDO saveCoreCustAnnulBill(final String refNo, final String version) {

        BTAssert.notNull(refNo, "凭证单号为空!操作失败");
        BTAssert.notNull(version, "数据版本为空!操作失败");
        logger.info("begin to saveCoreCustAnnulBill bill" + UserUtils.getOperatorInfo().getName());
        ScfAcceptBillDO bill = this.selectOneWithVersion(refNo, version);
        BTAssert.notNull(bill, "此票据异常!操作失败");
        if (!VersionConstantCollentions.BILL_MODE_TYPE_PAPER_MONEY.equals(bill.getBillMode())) {
            BTAssert.notNull(null, "票据回收只能是纸票!当前票据不符合条件");
        }
        Collection<CustInfo> custInfos = custMechBaseService.queryCustInfoByOperId(UserUtils.getOperatorInfo().getId());
        List<Long> coreCustNoList = getCustNoList(custInfos);
        // bill.getCoreCustNo().
        if (!coreCustNoList.contains(bill.getCoreCustNo())) {
            BTAssert.notNull(null, "你没有相应的权限!操作失败");
        }
        bill = this.annulEffectiveOperator(bill);
        logger.info("success to saveCoreCustAnnulBill bill" + UserUtils.getOperatorInfo().getName());
        return bill;
    }

    /**
     * 查询票据详情
     * @param anRefNo
     * @param anVersion
     * @return
     */
    public ScfAcceptBillDO findBill(final String anRefNo, final String anVersion) {

        BTAssert.notNull(anRefNo, "凭证单号为空!操作失败");
        BTAssert.notNull(anVersion, "数据版本为空!操作失败");
        logger.info("begin  to find bill" + UserUtils.getOperatorInfo().getName());
        ScfAcceptBillDO bill = this.selectOneWithVersion(anRefNo, anVersion);
        BTAssert.notNull(bill, "此票据异常!操作失败");
        logger.info("success to find bill" + UserUtils.getOperatorInfo().getName());
        return bill;
    }

    /**
     * 票据核准
     * @param anRefNo
     * @param anVersion
     * @return
     */
    public ScfAcceptBillDO saveAuditBill(final String anRefNo, final String anVersion) {

        BTAssert.notNull(anRefNo, "凭证单号为空!操作失败");
        BTAssert.notNull(anVersion, "数据版本为空!操作失败");
        logger.info("begin to audit bill" + UserUtils.getOperatorInfo().getName());
        ScfAcceptBillDO bill = this.selectOneWithVersion(anRefNo, anVersion);
        BTAssert.notNull(bill, "此票据异常!操作失败");
        checkStatus(UserUtils.getOperatorInfo().getOperOrg(), bill.getCoreOperOrg(), false, "您没有审核权限!操作失败");
        this.auditOperator(UserUtils.getOperatorInfo(), bill);
        logger.info("success to audit bill" + UserUtils.getOperatorInfo().getName());
        return bill;

    }

    /**
     * 查询未生效的票据信息
     * @param anMap 查询条件
     * @param anIsOnlyNormal 是否只查询用户登入的数据
     * @param anFlag 是否需要查询总的页数
     * @param anPageNum 当前页数
     * @param anPageSize 每页显示的数量
     * anAuditFlag true 是核准数据的查询  false 是登入列表的查询
     * @return
     */
    public Page<ScfAcceptBillDO> queryIneffectiveBill(Map<String, Object> anMap, final String anIsOnlyNormal,
            final String anFlag, final int anPageNum, final int anPageSize, final boolean anAuditFlag) {

        BTAssert.notNull(anMap, "查询条件为空!操作失败");
        // 操作员只能查询本机构数据
        // anMap.put("operOrg", UserUtils.getOperatorInfo().getOperOrg());
        // 只查询数据非自动生成的数据来源
        if ("1".equals(anIsOnlyNormal)) {
            anMap.put("dataSource", "1");
        }
        // 去除空白字符串的查询条件
        anMap = Collections3.filterMapEmptyObject(anMap);
        // 查询未生效的单据核心企业必须有对应的企业
        BTAssert.notNull(anMap.get("coreCustNo"), "请选择你所在的企业再查询!");
        // 模糊查询
        // anMap = Collections3.fuzzyMap(anMap, new String[] { "billNo"});
        if (anMap.containsKey("LIKEbillNo") && null != anMap.get("LIKEbillNo")) {
            Object object = anMap.get("LIKEbillNo");
            anMap.put("LIKEbillNo", "%" + object + "%");
        }
        anMap.put("coreOperOrg", UserUtils.getOperatorInfo().getOperOrg());

        if (anAuditFlag) {

            if (!anMap.containsKey("coreCustNo") || anMap.get("coreCustNo") == null
                    || StringUtils.isBlank(anMap.get("coreCustNo").toString())) {

                Collection<CustInfo> custInfos = custMechBaseService
                        .queryCustInfoByOperId(UserUtils.getOperatorInfo().getId());
                anMap.put("coreCustNo", getCustNoList(custInfos));
            }
            anMap.put("docStatus", VersionConstantCollentions.DOC_STATUS_CONFIRM);
        } else {

            anMap.put("operId", UserUtils.getOperatorInfo().getId());
        }

        Page<ScfAcceptBillDO> billList = this.selectPropertyIneffectiveByPageWithVersion(anMap, anPageNum, anPageSize,
                "1".equals(anFlag), "refNo desc");

        return billList;
    }

    /**
     * 将给定的企业集合中提取企业的id
     * @param custInfos
     * @return
     */
    private List<Long> getCustNoList(final Collection<CustInfo> custInfos) {

        List<Long> custNos = new ArrayList<>();
        for (CustInfo custInfo : custInfos) {
            custNos.add(custInfo.getCustNo());
        }

        return custNos;

    }

    /**
     * 查询已经生效的票据信息
     * @param anMap 查询条件
     * @param anIsOnlyNormal 数据来源是否只查询用户登入的数据
     * @param anFlag  是否查询总的数据量
     * @param anPageNum 每页显示的数据量
     * @param anPageSize 总的页数
     * @param anIsCust true 是供应商的已经生效的查询    false 是核心企业的已经生效的查询
     * @return
     */
    public Page<ScfAcceptBillDO> queryEffectiveBill(Map<String, Object> anMap, final String anIsOnlyNormal,
            final String anFlag, final int anPageNum, final int anPageSize, final boolean anIsCust) {

        BTAssert.notNull(anMap, "查询条件为空!操作失败");
        // 操作员只能查询本机构数据
        // anMap.put("operOrg", UserUtils.getOperatorInfo().getOperOrg());
        // 只查询数据非自动生成的数据来源
        if ("1".equals(anIsOnlyNormal)) {
            anMap.put("dataSource", "1");
        }

        // 去除空白字符串的查询条件
        anMap = Collections3.filterMapEmptyObject(anMap);
        // 模糊查询
        if (anMap.containsKey("LIKEbillNo") && null != anMap.get("LIKEbillNo")) {
            Object object = anMap.get("LIKEbillNo");
            anMap.put("LIKEbillNo", "%" + object + "%");
        }

        Collection<CustInfo> custInfos = custMechBaseService.queryCustInfoByOperId(UserUtils.getOperatorInfo().getId());
        if (anIsCust) {

            if (!anMap.containsKey("supplierNo") || anMap.get("supplierNo") == null
                    || StringUtils.isBlank(anMap.get("supplierNo").toString())) {
                anMap.put("supplierNo", getCustNoList(custInfos));
            }

            // 供应商去除已经废止的票据信息
            anMap.put("NEbusinStatus", VersionConstantCollentions.BUSIN_STATUS_ANNUL);
            // 票据/应收账款，在未生效时 已过期，供应商应无法查看到。
            anMap.put("NEexpireFlagStatus", VersionConstantCollentions.EXPIRE_FLAG_STATUS_INEFFECTIVE);
        } else {

            if (!anMap.containsKey("coreCustNo") || anMap.get("coreCustNo") == null
                    || StringUtils.isBlank(anMap.get("coreCustNo").toString())) {
                anMap.put("coreCustNo", getCustNoList(custInfos));
            }
            anMap.put("coreOperOrg", UserUtils.getOperatorInfo().getOperOrg());
            return this.selectPropertyByPageWithVersion(anMap, anPageNum, anPageSize, "1".equals(anFlag), "id desc");
        }

        Page<ScfAcceptBillDO> billList = this.selectPropertyEffectiveByPageWithVersion(anMap, anPageNum, anPageSize,
                "1".equals(anFlag), "id desc");

        return billList;
    }

    /**
     * 根据当前公司废止当前单据内容
     * @param anMap 查询条件
     * @param anIsOnlyNormal 1 只查询当前系统登入的数据
     * @param anFlag 是否需要查询总的数量
     * @param anPageNum 当前页数
     * @param anPageSize 每页显示的数据量
     * @return
     */

    public Page<ScfAcceptBillDO> queryCanAnnulBill(Map<String, Object> anMap, final String anIsOnlyNormal,
            final String anFlag, final int anPageNum, final int anPageSize) {

        BTAssert.notNull(anMap, "查询条件为空!操作失败");
        // 操作员只能查询本机构数据
        // anMap.put("operOrg", UserUtils.getOperatorInfo().getOperOrg());
        // 只查询数据非自动生成的数据来源
        if ("1".equals(anIsOnlyNormal)) {
            anMap.put("dataSource", "1");
        }
        // 去除空白字符串的查询条件
        anMap = Collections3.filterMapEmptyObject(anMap);
        // 模糊查询
        if (anMap.containsKey("LIKEbillNo") && null != anMap.get("LIKEbillNo")) {
            Object object = anMap.get("LIKEbillNo");
            anMap.put("LIKEbillNo", "%" + object + "%");
        }
        // anMap = Collections3.fuzzyMap(anMap, new String[] { "billNo"});

        if (!anMap.containsKey("coreCustNo") || anMap.get("coreCustNo") == null
                || StringUtils.isBlank(anMap.get("coreCustNo").toString())) {

            Collection<CustInfo> custInfos = custMechBaseService
                    .queryCustInfoByOperId(UserUtils.getOperatorInfo().getId());
            anMap.put("coreCustNo", getCustNoList(custInfos));
        }
        // 票据回收只能是生效并且是纸票 BILL_MODE_TYPE_PAPER_MONEY
        anMap.put("billMode", VersionConstantCollentions.BILL_MODE_TYPE_PAPER_MONEY);
        Page<ScfAcceptBillDO> billList = this.selectPropertyCanAunulByPageWithVersion(anMap, anPageNum, anPageSize,
                "1".equals(anFlag), "refNo desc");

        return billList;
    }

    public List<ScfAcceptBillDO> saveResolveFile(final List<Map<String, Object>> listMap) {

        return null;
    }

    /**
     * 更新票据的附件信息
     * @param anFileList
     * @param anRefNo
     * @param anVersion
     * @return
     */
    public ScfAcceptBillDO saveModifyAcceptBillDOFile(final String anFileList, final String anRefNo,
            final String anVersion) {

        BTAssert.notNull(anRefNo, "修改条件为空!操作失败");
        BTAssert.notNull(anVersion, "修改条件为空!操作失败");
        ScfAcceptBillDO acceptBillDO = this.selectOneWithVersion(anRefNo, anVersion);
        BTAssert.notNull(acceptBillDO, "没有找到相应的票据信息!请刷新重试");

        checkBillDOUploadFileStatus(acceptBillDO, UserUtils.getOperatorInfo());
        // 保存附件信息
        if (StringUtils.isNotBlank(anFileList)) {
            acceptBillDO.setBatchNo(
                    custFileDubboService.updateAndDelCustFileItemInfo(anFileList, acceptBillDO.getBatchNo()));
        } else {
            acceptBillDO.setBatchNo(custFileDubboService.updateAndDelCustFileItemInfo("", acceptBillDO.getBatchNo()));
        }
        this.updateByPrimaryKeySelective(acceptBillDO);
        return acceptBillDO;
    }

    public void checkBillDOUploadFileStatus(final ScfAcceptBillDO anBill, final CustOperatorInfo anOperatorInfo) {

        checkStatus(anBill.getBusinStatus(), VersionConstantCollentions.BUSIN_STATUS_ANNUL, true, "当前单据已经废止，不能再次上传附件");
        checkStatus(anBill.getBusinStatus(), VersionConstantCollentions.BUSIN_STATUS_EXPIRE, true, "当前单据已经过期，不能再次上传附件");
        checkStatus(anBill.getBusinStatus(), VersionConstantCollentions.BUSIN_STATUS_TRANSFER, true, "当前单据转让，不能再次上传附件");
        checkStatus(anBill.getOperOrg(), anOperatorInfo.getOperOrg(), false, "你没有当前记录的操作权限");
        checkStatus(anBill.getLockedStatus(), VersionConstantCollentions.LOCKED_STATUS_LOCKED, true, "当前票据已经使用，不能再次更改");
    }

    /**
     * 检查状态信息
     */
    @Override
    public void checkStatus(final String anBusinStatus, final String anTargetStatus, final boolean anFlag,
            final String anMessage) {
        if (StringUtils.equals(anBusinStatus, anTargetStatus) == anFlag) {
            logger.warn(anMessage);
            throw new BytterTradeException(40001, anMessage);
        }
    }

    /**
     * 定时任务自动处理过期的票据信息
     */
    public void saveExpireEndDataList() {

        List<ScfAcceptBillDO> endDataList = this.queryEndedDataList();
        if (!Collections3.isEmpty(endDataList)) {

            int size = endDataList.size();
            int numEach = size % LogConstantCollections.LOG_RECORD_SIZE == 0
                    ? size / LogConstantCollections.LOG_RECORD_SIZE : size / LogConstantCollections.LOG_RECORD_SIZE + 1;
            for (int i = 0; i < numEach; i++) {

                if (i + 1 == numEach) {
                    saveExpireList(endDataList.subList(LogConstantCollections.LOG_RECORD_SIZE * i, size),
                            AssetConstantCollentions.ASSET_BASEDATA_INFO_TYPE_BILL, i + 1 + "");

                } else {

                    saveExpireList(
                            endDataList.subList(LogConstantCollections.LOG_RECORD_SIZE * i,
                                    LogConstantCollections.LOG_RECORD_SIZE * (i + 1)),
                            AssetConstantCollentions.ASSET_BASEDATA_INFO_TYPE_BILL, i + 1 + "");

                }

            }

        }

    }

    /**
     * 
     * @Title: queryWorkbenchBill 
     * @Description: 供应商，核心企业查询票据登记生效，未使用票据 
     * @param anMap 前端参数
     * @param anIsCust 查询客户类型，ture 供应商查询，false资金方查询
     * @return 票据登记生效，未使用票据 
     * @throws 
     * @author xuzx
     * @date 2017年11月22日 上午9:40:49
     */
    public List<ScfAcceptBillDO> queryNotUserBillList(Map<String, Object> anParam, final boolean anIsCust) {
        // 查询当前登录的用户下企业信息
        Collection<CustInfo> custInfos = custMechBaseService.queryCustInfoByOperId(UserUtils.getOperatorInfo().getId());
        Map<String, Object> anMap = setCompanyNo(anParam, anIsCust, custInfos);
        // 供应商查询状态生效状态，核心企业查询登记，生效状态
        if (anIsCust) {
            anMap.put("businStatus", 1);
        } else {
            anMap.put("businStatus", new String[] { "0", "1" });
        }
        return this.selectByProperty(anMap);

    }

    /**
     * 
     * @Title: queryNotUserBillPage 
     * @Description:  分页查询生效,未使用票据
     * @param anMap 前端参数
     * @param pageNum 页码
     * @param pageSize  页面大小
     * @param flag  是否统计总数
     * @param anIsCust  查询客户类型，ture 供应商查询，false资金方查询
     * @return 生效且未使用应收票据分页
     * @throws 
     * @author xuzx
     * @date 2017年11月22日 下午5:01:55
     */
    public Page<ScfAcceptBillDO> queryNotUserBillPage(Map<String, Object> anParam, final int pageNum,
            final int pageSize, final int flag, final boolean anIsCust) {
        // 查询当前登录的用户下企业信息
        Collection<CustInfo> custInfos = custMechBaseService.queryCustInfoByOperId(UserUtils.getOperatorInfo().getId());
        Map<String, Object> anMap = setCompanyNo(anParam, anIsCust, custInfos);
        // 供应商查询状态生效状态，核心企业查询登记，生效状态
        if (anIsCust) {
            anMap.put("businStatus", 1);
        } else {
            anMap.put("businStatus", new String[] { "0", "1" });
        }
        return this.selectPropertyByPage(anMap, pageNum, pageSize, flag == 1, "refNo desc");
    }

}
